#include "StdAfx.h"
#include "sha256.h"

/// same as reset()
using namespace KoishiSHA256;

SHA256::SHA256(){
	reset();
}
void SHA256::reset(){
	m_numBytes   = 0;
	m_bufferSize = 0;
	// according to RFC 1321
	m_hash[0] = 0x6a09e667;
	m_hash[1] = 0xbb67ae85;
	m_hash[2] = 0x3c6ef372;
	m_hash[3] = 0xa54ff53a;
	m_hash[4] = 0x510e527f;
	m_hash[5] = 0x9b05688c;
	m_hash[6] = 0x1f83d9ab;
	m_hash[7] = 0x5be0cd19;
}
inline dword KoishiSHA256::rotate(dword a, dword c){
	return (a >> c) | (a << (32 - c));
}
inline dword KoishiSHA256::swap(dword x){
	return _byteswap_ulong(x);
}
// mix functions for processBlock()
inline dword KoishiSHA256::f1(dword e, dword f, dword g){
	dword term1 = rotate(e, 6) ^ rotate(e, 11) ^ rotate(e, 25);
	dword term2 = (e & f) ^ (~e & g);
	return term1 + term2;
}
inline dword KoishiSHA256::f2(dword a, dword b, dword c){
	dword term1 = rotate(a, 2) ^ rotate(a, 13) ^ rotate(a, 22);
	dword term2 = ((a | b) & c) | (a & b);
	return term1 + term2;
}
/// process 64 bytes
void SHA256::processBlock(const void* data){
	// get last hash
	dword a = m_hash[0];
	dword b = m_hash[1];
	dword c = m_hash[2];
	dword d = m_hash[3];
	dword e = m_hash[4];
	dword f = m_hash[5];
	dword g = m_hash[6];
	dword h = m_hash[7];

	// data represented as 16x 32-bit words
	const dword* input = (dword*) data;
	// convert to big endian
	dword words[64];
	int i;
	for (i = 0; i < 16; i++)
#if defined(__BYTE_ORDER) && (__BYTE_ORDER != 0) && (__BYTE_ORDER == __BIG_ENDIAN)
		words[i] =      input[i];
#else
		words[i] = swap(input[i]);
#endif

	dword x,y; // temporaries
	// first round
	x = h + f1(e,f,g) + 0x428a2f98 + words[ 0]; y = f2(a,b,c); d += x; h = x + y;
	x = g + f1(d,e,f) + 0x71374491 + words[ 1]; y = f2(h,a,b); c += x; g = x + y;
	x = f + f1(c,d,e) + 0xb5c0fbcf + words[ 2]; y = f2(g,h,a); b += x; f = x + y;
	x = e + f1(b,c,d) + 0xe9b5dba5 + words[ 3]; y = f2(f,g,h); a += x; e = x + y;
	x = d + f1(a,b,c) + 0x3956c25b + words[ 4]; y = f2(e,f,g); h += x; d = x + y;
	x = c + f1(h,a,b) + 0x59f111f1 + words[ 5]; y = f2(d,e,f); g += x; c = x + y;
	x = b + f1(g,h,a) + 0x923f82a4 + words[ 6]; y = f2(c,d,e); f += x; b = x + y;
	x = a + f1(f,g,h) + 0xab1c5ed5 + words[ 7]; y = f2(b,c,d); e += x; a = x + y;

	// secound round
	x = h + f1(e,f,g) + 0xd807aa98 + words[ 8]; y = f2(a,b,c); d += x; h = x + y;
	x = g + f1(d,e,f) + 0x12835b01 + words[ 9]; y = f2(h,a,b); c += x; g = x + y;
	x = f + f1(c,d,e) + 0x243185be + words[10]; y = f2(g,h,a); b += x; f = x + y;
	x = e + f1(b,c,d) + 0x550c7dc3 + words[11]; y = f2(f,g,h); a += x; e = x + y;
	x = d + f1(a,b,c) + 0x72be5d74 + words[12]; y = f2(e,f,g); h += x; d = x + y;
	x = c + f1(h,a,b) + 0x80deb1fe + words[13]; y = f2(d,e,f); g += x; c = x + y;
	x = b + f1(g,h,a) + 0x9bdc06a7 + words[14]; y = f2(c,d,e); f += x; b = x + y;
	x = a + f1(f,g,h) + 0xc19bf174 + words[15]; y = f2(b,c,d); e += x; a = x + y;

	// extend to 24 words
	for (; i < 24; i++)
		words[i] = words[i-16] +
		(rotate(words[i-15],  7) ^ rotate(words[i-15], 18) ^ (words[i-15] >>  3)) +
		words[i-7] +
		(rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));

	// third round
	x = h + f1(e,f,g) + 0xe49b69c1 + words[16]; y = f2(a,b,c); d += x; h = x + y;
	x = g + f1(d,e,f) + 0xefbe4786 + words[17]; y = f2(h,a,b); c += x; g = x + y;
	x = f + f1(c,d,e) + 0x0fc19dc6 + words[18]; y = f2(g,h,a); b += x; f = x + y;
	x = e + f1(b,c,d) + 0x240ca1cc + words[19]; y = f2(f,g,h); a += x; e = x + y;
	x = d + f1(a,b,c) + 0x2de92c6f + words[20]; y = f2(e,f,g); h += x; d = x + y;
	x = c + f1(h,a,b) + 0x4a7484aa + words[21]; y = f2(d,e,f); g += x; c = x + y;
	x = b + f1(g,h,a) + 0x5cb0a9dc + words[22]; y = f2(c,d,e); f += x; b = x + y;
	x = a + f1(f,g,h) + 0x76f988da + words[23]; y = f2(b,c,d); e += x; a = x + y;

	// extend to 32 words
	for (; i < 32; i++)
		words[i] = words[i-16] +
		(rotate(words[i-15],  7) ^ rotate(words[i-15], 18) ^ (words[i-15] >>  3)) +
		words[i-7] +
		(rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));

	// fourth round
	x = h + f1(e,f,g) + 0x983e5152 + words[24]; y = f2(a,b,c); d += x; h = x + y;
	x = g + f1(d,e,f) + 0xa831c66d + words[25]; y = f2(h,a,b); c += x; g = x + y;
	x = f + f1(c,d,e) + 0xb00327c8 + words[26]; y = f2(g,h,a); b += x; f = x + y;
	x = e + f1(b,c,d) + 0xbf597fc7 + words[27]; y = f2(f,g,h); a += x; e = x + y;
	x = d + f1(a,b,c) + 0xc6e00bf3 + words[28]; y = f2(e,f,g); h += x; d = x + y;
	x = c + f1(h,a,b) + 0xd5a79147 + words[29]; y = f2(d,e,f); g += x; c = x + y;
	x = b + f1(g,h,a) + 0x06ca6351 + words[30]; y = f2(c,d,e); f += x; b = x + y;
	x = a + f1(f,g,h) + 0x14292967 + words[31]; y = f2(b,c,d); e += x; a = x + y;

	// extend to 40 words
	for (; i < 40; i++)
		words[i] = words[i-16] +
		(rotate(words[i-15],  7) ^ rotate(words[i-15], 18) ^ (words[i-15] >>  3)) +
		words[i-7] +
		(rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));

	// fifth round
	x = h + f1(e,f,g) + 0x27b70a85 + words[32]; y = f2(a,b,c); d += x; h = x + y;
	x = g + f1(d,e,f) + 0x2e1b2138 + words[33]; y = f2(h,a,b); c += x; g = x + y;
	x = f + f1(c,d,e) + 0x4d2c6dfc + words[34]; y = f2(g,h,a); b += x; f = x + y;
	x = e + f1(b,c,d) + 0x53380d13 + words[35]; y = f2(f,g,h); a += x; e = x + y;
	x = d + f1(a,b,c) + 0x650a7354 + words[36]; y = f2(e,f,g); h += x; d = x + y;
	x = c + f1(h,a,b) + 0x766a0abb + words[37]; y = f2(d,e,f); g += x; c = x + y;
	x = b + f1(g,h,a) + 0x81c2c92e + words[38]; y = f2(c,d,e); f += x; b = x + y;
	x = a + f1(f,g,h) + 0x92722c85 + words[39]; y = f2(b,c,d); e += x; a = x + y;

	// extend to 48 words
	for (; i < 48; i++)
		words[i] = words[i-16] +
		(rotate(words[i-15],  7) ^ rotate(words[i-15], 18) ^ (words[i-15] >>  3)) +
		words[i-7] +
		(rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));

	// sixth round
	x = h + f1(e,f,g) + 0xa2bfe8a1 + words[40]; y = f2(a,b,c); d += x; h = x + y;
	x = g + f1(d,e,f) + 0xa81a664b + words[41]; y = f2(h,a,b); c += x; g = x + y;
	x = f + f1(c,d,e) + 0xc24b8b70 + words[42]; y = f2(g,h,a); b += x; f = x + y;
	x = e + f1(b,c,d) + 0xc76c51a3 + words[43]; y = f2(f,g,h); a += x; e = x + y;
	x = d + f1(a,b,c) + 0xd192e819 + words[44]; y = f2(e,f,g); h += x; d = x + y;
	x = c + f1(h,a,b) + 0xd6990624 + words[45]; y = f2(d,e,f); g += x; c = x + y;
	x = b + f1(g,h,a) + 0xf40e3585 + words[46]; y = f2(c,d,e); f += x; b = x + y;
	x = a + f1(f,g,h) + 0x106aa070 + words[47]; y = f2(b,c,d); e += x; a = x + y;

	// extend to 56 words
	for (; i < 56; i++)
		words[i] = words[i-16] +
		(rotate(words[i-15],  7) ^ rotate(words[i-15], 18) ^ (words[i-15] >>  3)) +
		words[i-7] +
		(rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));

	// seventh round
	x = h + f1(e,f,g) + 0x19a4c116 + words[48]; y = f2(a,b,c); d += x; h = x + y;
	x = g + f1(d,e,f) + 0x1e376c08 + words[49]; y = f2(h,a,b); c += x; g = x + y;
	x = f + f1(c,d,e) + 0x2748774c + words[50]; y = f2(g,h,a); b += x; f = x + y;
	x = e + f1(b,c,d) + 0x34b0bcb5 + words[51]; y = f2(f,g,h); a += x; e = x + y;
	x = d + f1(a,b,c) + 0x391c0cb3 + words[52]; y = f2(e,f,g); h += x; d = x + y;
	x = c + f1(h,a,b) + 0x4ed8aa4a + words[53]; y = f2(d,e,f); g += x; c = x + y;
	x = b + f1(g,h,a) + 0x5b9cca4f + words[54]; y = f2(c,d,e); f += x; b = x + y;
	x = a + f1(f,g,h) + 0x682e6ff3 + words[55]; y = f2(b,c,d); e += x; a = x + y;

	// extend to 64 words
	for (; i < 64; i++)
		words[i] = words[i-16] +
		(rotate(words[i-15],  7) ^ rotate(words[i-15], 18) ^ (words[i-15] >>  3)) +
		words[i-7] +
		(rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));

	// eigth round
	x = h + f1(e,f,g) + 0x748f82ee + words[56]; y = f2(a,b,c); d += x; h = x + y;
	x = g + f1(d,e,f) + 0x78a5636f + words[57]; y = f2(h,a,b); c += x; g = x + y;
	x = f + f1(c,d,e) + 0x84c87814 + words[58]; y = f2(g,h,a); b += x; f = x + y;
	x = e + f1(b,c,d) + 0x8cc70208 + words[59]; y = f2(f,g,h); a += x; e = x + y;
	x = d + f1(a,b,c) + 0x90befffa + words[60]; y = f2(e,f,g); h += x; d = x + y;
	x = c + f1(h,a,b) + 0xa4506ceb + words[61]; y = f2(d,e,f); g += x; c = x + y;
	x = b + f1(g,h,a) + 0xbef9a3f7 + words[62]; y = f2(c,d,e); f += x; b = x + y;
	x = a + f1(f,g,h) + 0xc67178f2 + words[63]; y = f2(b,c,d); e += x; a = x + y;

	// update hash
	m_hash[0] += a;
	m_hash[1] += b;
	m_hash[2] += c;
	m_hash[3] += d;
	m_hash[4] += e;
	m_hash[5] += f;
	m_hash[6] += g;
	m_hash[7] += h;
}
/// add arbitrary number of bytes
void SHA256::add(const void* data, size_t numBytes)
{
	const uchar* current = (const uchar*) data;

	if (m_bufferSize > 0)
	{
		while (numBytes > 0 && m_bufferSize < BlockSize)
		{
			m_buffer[m_bufferSize++] = *current++;
			numBytes--;
		}
	}

	// full buffer
	if (m_bufferSize == BlockSize)
	{
		processBlock(m_buffer);
		m_numBytes  += BlockSize;
		m_bufferSize = 0;
	}

	// no more data ?
	if (numBytes == 0)
		return;

	// process full blocks
	while (numBytes >= BlockSize)
	{
		processBlock(current);
		current    += BlockSize;
		m_numBytes += BlockSize;
		numBytes   -= BlockSize;
	}

	// keep remaining bytes in buffer
	while (numBytes > 0)
	{
		m_buffer[m_bufferSize++] = *current++;
		numBytes--;
	}
}


/// process final block, less than 64 bytes
void SHA256::processBuffer()
{
	// the input bytes are considered as bits strings, where the first bit is the most significant bit of the byte

	// - append "1" bit to message
	// - append "0" bits until message length in bit mod 512 is 448
	// - append length as 64 bit integer

	// number of bits
	size_t paddedLength = m_bufferSize * 8;

	// plus one bit set to 1 (always appended)
	paddedLength++;

	// number of bits must be (numBits % 512) = 448
	size_t lower11Bits = paddedLength & 511;
	if (lower11Bits <= 448)
		paddedLength +=       448 - lower11Bits;
	else
		paddedLength += 512 + 448 - lower11Bits;
	// convert from bits to bytes
	paddedLength /= 8;

	// only needed if additional data flows over into a second block
	unsigned char extra[BlockSize];

	// append a "1" bit, 128 => binary 10000000
	if (m_bufferSize < BlockSize)
		m_buffer[m_bufferSize] = 128;
	else
		extra[0] = 128;

	size_t i;
	for (i = m_bufferSize + 1; i < BlockSize; i++)
		m_buffer[i] = 0;
	for (; i < paddedLength; i++)
		extra[i - BlockSize] = 0;

	// add message length in bits as 64 bit number
	ulongex msgBits = 8 * (m_numBytes + m_bufferSize);
	// find right position
	unsigned char* addLength;
	if (paddedLength < BlockSize)
		addLength = m_buffer + paddedLength;
	else
		addLength = extra + paddedLength - BlockSize;

	// must be big endian
	*addLength++ = (unsigned char)((msgBits >> 56) & 0xFF);
	*addLength++ = (unsigned char)((msgBits >> 48) & 0xFF);
	*addLength++ = (unsigned char)((msgBits >> 40) & 0xFF);
	*addLength++ = (unsigned char)((msgBits >> 32) & 0xFF);
	*addLength++ = (unsigned char)((msgBits >> 24) & 0xFF);
	*addLength++ = (unsigned char)((msgBits >> 16) & 0xFF);
	*addLength++ = (unsigned char)((msgBits >>  8) & 0xFF);
	*addLength   = (unsigned char)( msgBits        & 0xFF);

	// process blocks
	processBlock(m_buffer);
	// flowed over into a second block ?
	if (paddedLength > BlockSize)
		processBlock(extra);
}


/// return latest hash as 64 hex characters
std::string SHA256::getHash()
{
	// compute hash (as raw bytes)
	unsigned char rawHash[HashBytes];
	getHash(rawHash);

	// convert to hex string
	std::string result;
	result.reserve(2 * HashBytes);
	for (int i = 0; i < HashBytes; i++)
	{
		static const char dec2hex[16+1] = "0123456789ABCDEF";
		result += dec2hex[(rawHash[i] >> 4) & 15];
		result += dec2hex[ rawHash[i]       & 15];
	}

	return result;
}


/// return latest hash as bytes
void SHA256::getHash(unsigned char buffer[SHA256::HashBytes])
{
	// save old hash if buffer is partially filled
	dword oldHash[HashValues];
	for (int i = 0; i < HashValues; i++)
		oldHash[i] = m_hash[i];

	// process remaining bytes
	processBuffer();

	unsigned char* current = buffer;
	for (int i = 0; i < HashValues; i++)
	{
		*current++ = (m_hash[i] >> 24) & 0xFF;
		*current++ = (m_hash[i] >> 16) & 0xFF;
		*current++ = (m_hash[i] >>  8) & 0xFF;
		*current++ =  m_hash[i]        & 0xFF;

		// restore old hash
		m_hash[i] = oldHash[i];
	}
}


/// compute SHA256 of a memory block
std::string SHA256::operator()(const void* data, size_t numBytes)
{
	reset();
	add(data, numBytes);
	return getHash();
}


/// compute SHA256 of a string, excluding final zero
std::string SHA256::operator()(const std::string& text)
{
	reset();
	add(text.c_str(), text.size());
	return getHash();
}
